ACM/ICPC 之 拓扑排序范例(POJ1094-POJ2585)
两道拓扑排序问题的范例,用拓扑排序解决的实质是一个单向关系问题
POJ1094(ZOJ1060)-Sortng It All Out
题意简单,但需要考虑的地方很多,因此很容易将code写繁琐了,会给力求code精简的强迫症患者一个警醒- -
题意:给出m组逻辑关系式,求n个字母间的排序,分排序成功-排序矛盾-不能确定三种情况输出相应语句
题解:拓扑排序,访问入度为0的结点,邻接点入度-1,然后继续访问入度为0的结点...直到访问完成为止
需要注意的地方在于三种情况根据最早确定的情况来输出,例如先排序完成,但后面的关系式表明排序矛盾,此时按照先排序完成得到的序列输出。
1 //一道考查拓扑排序的经典例题 2 //POJ1094-ZOJ1060 3 //Time:0Ms Memory:188K 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<vector> 8 using namespace std; 9 10 #define MAX 27 11 12 struct Letter { 13 int in; //入度 14 int v; 15 vector<int> p; 16 }letter[MAX]; 17 18 int n, m; 19 char seq[MAX]; 20 21 int topSort(int total) 22 { 23 memset(seq, 0, sizeof(seq)); 24 int tmp[MAX]; //临时入度数组 25 for (int i = 0; i < n; i++) 26 tmp[i] = letter[i].in; 27 28 bool correct = true; //是否可行 29 for (int i = 0; i < total;i++) 30 { 31 int cur; //当前字母位置 32 int cnt = 0; //入度为0的个数 33 for (int i = 0; i < n; i++) 34 if (tmp[i] == 0) 35 { 36 cur = i; 37 cnt++; 38 } 39 if (cnt == 0) return -1; 40 if (cnt > 1) correct = false; //多选时-不可行但须判断是否矛盾 41 for (int i = 0; i < letter[cur].p.size(); i++) 42 tmp[letter[cur].p[i]]--; 43 seq[i] = cur + 'A'; 44 tmp[cur]--; 45 } 46 return correct; 47 } 48 49 int main() 50 { 51 //freopen("in.txt", "r", stdin); 52 while (scanf("%d%d", &n, &m), n && m) 53 { 54 int total = 0; //输入字母总个数 55 bool flag = false; //得到结论 56 memset(letter, 0, sizeof(letter)); 57 for (int i = 1; i <= m; i++) 58 { 59 char formula[4]; 60 scanf("%s", formula); 61 if (flag) continue; //已得出结论 62 int small = formula[0] - 'A'; 63 int big = formula[2] - 'A'; 64 total += !letter[small].v + !letter[big].v; 65 letter[small].v = letter[big].v = 1; 66 letter[small].p.push_back(big); 67 letter[big].in++; 68 int key = topSort(total); 69 if (key == -1) 70 { 71 printf("Inconsistency found after %d relations.\n", i); 72 flag = true; 73 } 74 else if (key == 1) 75 { 76 printf("Sorted sequence determined after %d relations: %s.\n", i, seq); 77 flag = true; 78 } 79 } 80 if (!flag) 81 printf("Sorted sequence cannot be determined.\n"); 82 } 83 return 0; 84 }
POJ2585(ZOJ2193)-Window Pains
题意:给定一组出现在固定位置的预设窗口,求可否使得这些窗口按照先后次序得到给定样例的显示状态。
题解:拓扑排序,用覆盖表示先后关系,若最终无环则可以按照一定次序得到样例,如果有环,那么就与覆盖这种单向关系矛盾。
1 //拓扑排序-窗口覆盖问题 2 //单向关系问题 3 //Time:16Ms Memory:176K 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<vector> 8 #include<queue> 9 using namespace std; 10 11 #define MAX 4 12 13 //预设窗口 14 struct Map { 15 int size; //覆盖个数 16 int num[MAX]; 17 }map[MAX][MAX]; 18 19 struct Area { 20 vector<int> cover; 21 int in; //入度 22 }area[10]; 23 24 int win[MAX][MAX]; //实际窗口 25 int mov[4][2] = { {0,0}, {0,1}, {1,0}, {1,1} }; 26 27 void init() 28 { 29 for (int i = 0; i < 3; i++) 30 for (int j = 0; j < 3; j++) 31 for (int k = 0; k < 4; k++) 32 { 33 int tx = i + mov[k][0]; 34 int ty = j + mov[k][1]; 35 map[tx][ty].num[map[tx][ty].size++] = i * 3 + j + 1; 36 } 37 } 38 39 //拓扑排序 40 bool topology() 41 { 42 for (int i = 0; i < 9;i++) 43 { 44 int cur = 0; 45 while (++cur < 10 && area[cur].in); //找出入度为0的点 46 if (cur == 10) return false; //存在环-非单向关系 47 area[cur].in--; 48 49 vector<int> cover = area[cur].cover; 50 for (int i = 0; i < cover.size(); i++) 51 area[cover[i]].in--; 52 } 53 return true; 54 } 55 56 int main() 57 { 58 init(); 59 60 char command[12]; 61 while (scanf("%s", command), strcmp(command, "ENDOFINPUT")) 62 { 63 memset(area, 0, sizeof(area)); 64 for (int i = 0; i < 4; i++) 65 for (int j = 0; j < 4; j++) 66 scanf("%d", &win[i][j]); 67 scanf("%s", command); 68 69 //Area-Cover 70 for (int i = 0; i < 4; i++) 71 for (int j = 0; j < 4; j++) 72 for (int k = 0; k < map[i][j].size; k++) 73 { 74 vector<int> cover = area[win[i][j]].cover; 75 int num = map[i][j].num[k]; 76 if (num != win[i][j] && cover.end() == find(cover.begin(), cover.end(), num)) //去重 77 { 78 area[win[i][j]].cover.push_back(num); 79 area[num].in++; 80 } 81 } 82 83 if (topology()) 84 printf("THESE WINDOWS ARE CLEAN\n"); 85 else printf("THESE WINDOWS ARE BROKEN\n"); 86 } 87 88 return 0; 89 }
他坐在湖边,望向天空,她坐在对岸,盯着湖面